home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / flying-6.11 / ball.C < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-30  |  12.9 KB  |  585 lines

  1. #ifndef _global_h
  2. #    include "global.h"
  3. #endif
  4.  
  5. #ifndef _ball_h
  6. #    include "ball.h"            // Klassen-Info
  7. #endif
  8. #ifndef _game_h
  9. #    include "game.h"            // Info-Weitergabe bei Kollisionen/Start/Stop
  10. #endif
  11. #ifndef _mover_h
  12. #    include "mover.h"            // Optimierer fⁿr bewegte BΣlle
  13. #endif
  14. #ifndef _pball_h
  15. #    include "pball.h"            // als Callback bei Bewegungen
  16. #endif
  17.  
  18. #ifdef DEBUG
  19. #    ifndef _graph_h
  20. #        include "graph.h"    // Test auf Bereichsⁿberschreitungen
  21. #    endif
  22. #endif
  23.  
  24. //
  25. // ============================================================================
  26. // class Ball: fliegende Kugel
  27. // ============================================================================
  28. //
  29.  
  30. Ball::Ball( double x, double y, double vx, double vy, double r, double m ) :
  31. StaticBall(x,y,r), run_flag(0), a(g->GetPresetA()),
  32. #if (SIM_SLOW)
  33. next_slowstep(SUPPRESS_SLOWSTEP),
  34. #endif
  35. pball(0), m(m)
  36. {
  37.     type = BallObj;
  38.     SetV(Vec2(vx, vy));
  39.     dyn_id = DynObj::id;                // aktuelle DynID ins Objekt kopieren
  40.     state=0;
  41. }
  42.  
  43. Ball::Ball( double x, double y, double vx, double vy, double r ) :
  44. StaticBall(x,y,r), run_flag(0), a(g->GetPresetA()),
  45. #if (SIM_SLOW)
  46. next_slowstep(SUPPRESS_SLOWSTEP),
  47. #endif
  48. pball(0), m((R()*R()*R())/8.)
  49. {
  50.     type = BallObj;
  51.     SetV(Vec2(vx, vy));
  52.     dyn_id = DynObj::id;                // aktuelle DynID ins Objekt kopieren
  53.     state=0;
  54. }
  55.  
  56. Ball::Ball( double x, double y, double vx, double vy ) :
  57. StaticBall(x,y,g->GetNormalBallSize()),
  58. run_flag(0),
  59. a(g->GetPresetA()),
  60. #if (SIM_SLOW)
  61. next_slowstep(SUPPRESS_SLOWSTEP),
  62. #endif
  63. pball(0),
  64. m((R()*R()*R())/8.)
  65. {
  66.     type = BallObj;
  67.     SetV(Vec2(vx, vy));
  68.     dyn_id = DynObj::id;                // aktuelle DynID ins Objekt kopieren
  69.     state=0;
  70. }
  71.  
  72. Ball::Ball( const Vec2 &v, double r, double m ) :
  73. StaticBall(v,r), run_flag(0), a(g->GetPresetA()),
  74. #if (SIM_SLOW)
  75. next_slowstep(SUPPRESS_SLOWSTEP),
  76. #endif
  77. pball(0),
  78. m(m)
  79. {
  80.     type = BallObj;
  81.     SetV(Vec2Zero);
  82.     dyn_id = DynObj::id;                // aktuelle DynID ins Objekt kopieren
  83.     state=0;
  84. }
  85.  
  86. Ball::~Ball() {
  87.     if (state)        delete state;
  88. }
  89.  
  90. //
  91. // Reset wird direkt vor dem Start der Animation aufgerufen, um die SlowStep-
  92. // Berechnung zu initialisieren.
  93. //
  94. void Ball::Reset() {
  95. #if (SIM_SLOW)
  96.     last_slowstep = current_time;
  97.     slow_granoff  = g->GetSlowGranularity()*(double)((rand()%10000)/10000);
  98.     if (V().IsZero())        next_slowstep=0.0;
  99.     else                        next_slowstep=current_time+slow_granoff;
  100. #endif
  101.     if (!state)        state = new BallStateTop;
  102. }
  103.  
  104. void Ball::Redraw() {
  105.     state->Redraw();
  106. }
  107.  
  108. void Ball::SetV( const Vec2 &v_in )    {
  109.     v = v_in;
  110. #if (SIM_SLOW)
  111.     if (next_slowstep==SUPPRESS_SLOWSTEP)        return;
  112. #endif
  113.     if (IsRunning()) {
  114.         if (v.IsZero()) {
  115.             run_flag = 0;
  116.             g->StopBall(this);
  117.         }
  118.     }
  119.     else {
  120.         if (!v.IsZero()) {
  121.             run_flag = 1;
  122.             g->StartBall(this);
  123.         }
  124.     }
  125.                 
  126. #if (SIM_SLOW)
  127.     SlowStep();
  128. #endif
  129. }
  130.  
  131.  
  132. Vec2 Ball::Dir() const {
  133.     if (IsRunning())        return V().Norm1();
  134.     else                        return Vec2Zero;
  135. }
  136.  
  137. //
  138. // Setzten der neuen Position mit Aktualisierung auf Bildschirm
  139. //
  140. void Ball::SetP( const Vec2 &new_p ) {
  141.     state->MoveTo( new_p );
  142.     p=new_p;
  143. }
  144.  
  145. //
  146. // Die Move-Routine verschiebt den Ball gemΣ▀ seiner Geschwindigkeit und
  147. // der verschrittenen Zeit und fⁿhrt die Aktualisierung auf dem Bildschirm aus.
  148. //
  149. void Ball::Move( Real t ) {
  150. //
  151. // Positionsbestimmung
  152. //
  153. Vec2 new_p=p+V()*t;
  154.  
  155.     SetP( new_p );
  156.     if (pball)        pball->CueMoved();
  157.  
  158. //
  159. // Gegebenenfalls Test auf Bildschirmⁿberschreitung
  160. //
  161. #ifdef DEBUG
  162.     if (debug&CheckBoundary) {
  163.         if ((PX()-R()<0.0)||(PX()+R()>Real(max_x))||
  164.              (PY()-R()<0.0)||(PY()+R()>Real(max_y))) {
  165.             if ((PX()-R()<-EPS)||(PX()+R()-Real(max_x)>EPS)||
  166.                  (PY()-R()<-EPS)||(PY()+R()-Real(max_y)>EPS)) {
  167.                 printf( "*** BOUNDARY crossed by: " );
  168.                 this->Info();
  169.             }
  170. #    if (0)
  171.             else {
  172.                 printf( "    BOUNDARY touched by: " );
  173.                 this->Info();
  174.             }
  175. #    endif
  176.         }
  177.     }
  178. #endif
  179.  
  180. }
  181.  
  182. void Ball::Info() {
  183.     printf( "%02d: Ball:       %08lx: P()=(%4.1f,%4.1f), R()=%3.1f, V()=(%5.1f,%5.1f)\n",
  184.                 Object::id, (unsigned long)this,
  185.                 (double)PX(), (double)PY(), (double)R(),
  186.                 (double)VX(), (double)VY() );
  187. }
  188.  
  189. void Ball::TellPressed() {
  190. Ball    *ball;
  191. Real    dist;
  192.  
  193.     for (ball=(Ball*)DynObj::dyn_queue; ball; ball=(Ball*)ball->DynObj::next ) {
  194.         if (ball!=this) {
  195.             dist = (ball->P()-P()).Norm();        // absoluter Wert egal
  196.             if (dist<ball->R()+R()+0.02) {
  197.                 g->PressedBall(ball);
  198.             }
  199.         }
  200.     }
  201. }
  202.  
  203. #if (!TIME_CACHE)
  204. //
  205. // NextCollision
  206. // - Alle Objekte werden befragt, wann sie vom angegebenen Ball getroffen werden
  207. // - Es wⁿrde reichen, die Liste ab dem aktuellen Ball zu testen, da die vorher
  208. //   eingereihten Objekte bereits einen Test gegen den aktuellen Ball gemacht
  209. //   haben.
  210. //
  211. Real Ball::NextCollision() {
  212. Object    *obj;
  213. Real    next_time;
  214.  
  215. //
  216. // alle Objekte
  217. //
  218.     for (obj=stat_queue; obj; obj=obj->Object::next ) {
  219.         next_time = obj->HitFromBall(this)+current_time;
  220.         if ( next_time < min_time ) {
  221.                 min_time   = next_time;
  222.                 hit_ball   = this;
  223.                 hit_object = obj;
  224.         }
  225.     }
  226.     return min_time;
  227. }
  228. #else
  229.  
  230. //
  231. // NextCollision
  232. // - Die NextCollision-Version bei eingeschaltetem TimeCache mu▀ nur noch
  233. //   seine Werte mit den bisherigen Bestwerten vergleichen.
  234. //
  235. Real Ball::NextCollision() {
  236.     if (collision_time<min_time) {
  237.         min_time   = collision_time;
  238.         hit_ball   = this;
  239.         hit_object = collision_object;
  240.     }
  241.     return min_time;
  242. }
  243.  
  244. //
  245. // CollisionCalc berechnet den ZeitenCache neu, indem er jedes Objekt
  246. // mit der HitFromBall-Routine nach der nΣchsten Kollision befragt. Die
  247. // Zeiten der dynamischen Objekte sind dabei prinzipiell doppelt gespeichert,
  248. // da sie jeweils in den Cache's beider betroffener Objekte vorhanden sind.
  249. // Um sie nicht mehrfach zu berechnen, wird die Zeit den anderen beteiligten
  250. // Objekten mit Hilf der HitYouAt-Routine mitgeteilt.
  251. //
  252. void Ball::CollisionCalc() {
  253. int i;
  254.  
  255.     collision_time         = MAX_TIME;
  256.     collision_object    = 0;
  257.  
  258. #ifdef DEBUG
  259.     if (debug&CollCalc) {
  260.         printf( "CollCalc: " ); Info();
  261.     }
  262. #endif
  263.  
  264.     obj_list.StartRecalc();
  265.     for (i=0;i<col_objects;i++) {
  266.         if (obj_list[i]->dyn_id<=dyn_id) {
  267.             Real time = obj_list[i]->HitFromBall(this);
  268.             if (time<MAX_TIME)        time+=current_time;
  269.  
  270.             if (time<collision_time) {
  271.                 collision_time        = time;
  272.                 collision_object    = obj_list[i];
  273.                 obj_list.ToFront(i);
  274.             }
  275.             else {
  276.                 obj_list.ToBack(i);
  277.             }
  278.             obj_list.SetTime(i,time);
  279.         }
  280.         else {
  281.         //    Das Objekt darf nicht auf DynObj gecastet werden.
  282.             Real time = obj_list[i]->HitFromBall(this);
  283.             if (time<MAX_TIME)        time+=current_time;
  284.             ((Ball*)obj_list[i])->HitYouAt( this, time );
  285.             obj_list.SetTime(i,time);
  286.         }    
  287.     }
  288. #ifdef DEBUG
  289.     if (debug&CollCalc) {
  290.         if (collision_time<MAX_TIME) {
  291.                 printf( "  Target: " );    collision_object->Info();
  292.         }
  293.         else    printf( "  Target: None\n" );
  294.     }
  295. #endif
  296.     obj_list.StopRecalc();
  297. }
  298.  
  299.  
  300. //
  301. // HitYouAt sagt einem Ball, da▀ der Kollisionzeit von dem ⁿbergebenen Objekt
  302. // neu bestimmt wurde und daher in den Cache ⁿbernommen werden kann. Falls
  303. // das Objekt bisher das 'Target' der nΣchsten Kollision war, mu▀ das Target
  304. // neu bestimmt werden.
  305. //
  306. void Ball::HitYouAt( Object *obj, Real time ) {
  307.     obj_list.SetTimeDirect(obj->id,time);
  308.     if (time<collision_time) {
  309.         collision_time        = time;
  310.         collision_object    = obj;
  311. #ifdef DEBUG
  312.         if (debug&CollCalc) {
  313.             printf( "... CollCalc Hit: " );    Info();
  314.             printf( "...   New Target: " );    collision_object->Info();
  315.         }
  316. #endif
  317.     }
  318.     else if (collision_object==obj) {
  319.         collision_time        = MAX_TIME;
  320.         collision_object    = 0l;
  321.         for (int i=0;i<col_objects;i++) {
  322.             if (obj_list[i]->dyn_id<=dyn_id) {
  323. #if (ABORT_CALC)
  324.                 if (obj_list.GetTime(i)==NOT_REACHABLE) {
  325.                     Real time = obj_list[i]->HitFromBall(this);
  326.                     if (time<MAX_TIME)        time+=current_time;
  327. #ifdef DEBUG
  328.                     if (debug&AbortReCalc) {
  329.                         if (time==NOT_REACHABLE) {
  330.                             printf( "... CollCalc %d: Recalc for %d. (TOO FAR)\n",
  331.                                                             Object::id, obj_list[i]->id );
  332.                         }
  333.                         else {
  334.                             printf( "... CollCalc %d: Recalc for %d. (enhancement)\n",
  335.                                                             Object::id, obj_list[i]->id );
  336.                         }
  337.                     }
  338. #endif
  339.                     obj_list.SetTime(i,time);
  340.                 }
  341. #endif
  342.                 if (obj_list.GetTime(i)<collision_time) {
  343.                     collision_time        = obj_list.GetTime(i);
  344.                     collision_object    = obj_list[i];
  345.                 }
  346.             }
  347.         }
  348. #ifdef DEBUG
  349.         if (debug&CollCalc) {
  350.             printf( "... CollCalc Hit: " );    Info();
  351.             if (collision_time<MAX_TIME) {
  352.                     printf( "... Reset Target: " );    collision_object->Info();
  353.             }
  354.             else    printf( "... Reset Target: None\n" );
  355.         }
  356. #endif
  357.     }
  358. }
  359.  
  360. #endif
  361.  
  362.  
  363. //
  364. // HitFromBall berechnet die Zeit bis zum Zusammensto▀ mit der ⁿbergebenen
  365. // Kugel. Als Sonderbehandlung liefert er die Zeit bis zum Stillstand, bzw.
  366. // bis zum nΣchsten SlowStep, wenn er sich selbst als Parameter hat.
  367. //
  368. Real Ball::HitFromBall( Ball *b ) {
  369.     if (IsIdle()||b->IsIdle())        return MAX_TIME;
  370.  
  371.     if (this==b) {
  372. #if (SIM_SLOW)
  373.                 if (next_slowstep)    return next_slowstep-current_time;
  374. #endif
  375.             // Kugel steht bereits (oder wird nicht gebremst)
  376.                 return NO_HIT;
  377.     }
  378.     else {
  379. #                ifdef DEBUG
  380.                     if (debug&CheckBoundary) {
  381.                         Real dist = (P()-b->P()).Norm();
  382.                         if ( dist<R()+b->R() ) {
  383.                             if (dist-R()-b->R()<-2*EPS) {
  384.                                 printf( "overlap in distance %g:\n", dist );
  385.                                 printf( "         " ); Info();
  386.                                 printf( "         " ); b->Info();
  387.                                 CloseGraphic();
  388.                                 exit(0);
  389.                             }
  390.                         }
  391.                     }
  392. #                endif
  393.                 return OuterHitFromBall( b, b->V()-v );
  394.  
  395.     }
  396. }
  397.  
  398.  
  399. #if (SIM_SLOW)
  400.  
  401. //
  402. // Der Slowstep wird bei der entsprechenden Option eingefuegt, um die Kugel
  403. // gemΣ▀ der fⁿr sie gesetzten negativ-Beschleunigung zu bremsen. Das geschieht
  404. // in bestimmten Intervallen, der Maximalgr÷▀e 'SlowGranularity'. Damit die
  405. // Zeiten des Time-Cache sich nicht alle gleichzeitig verΣndern. Bei jeder
  406. // Kollision findet die Aktualisierung der Zeit explizit statt, da in diesem
  407. // Fall sowieso der Zeitenspeicher ungⁿltig wird.
  408. //
  409. void Ball::SlowStep() {
  410.  
  411.     if (IsRunning()) {
  412.         if (next_slowstep) {
  413.         Real    len = Len();
  414.         Real    new_len = len + a*(current_time-last_slowstep);
  415.                 if (new_len<RealZero)            new_len=RealZero;
  416.                 v *= new_len/len;
  417.                 last_slowstep=current_time;
  418.                 next_slowstep=current_time+g->GetSlowGranularity();
  419.         }
  420.         else {
  421.                 last_slowstep=current_time;
  422.                 next_slowstep=current_time+slow_granoff;
  423.         }
  424.     }
  425.     else {
  426.         if (next_slowstep) {
  427.             next_slowstep = RealZero;
  428.         }
  429.     }
  430. }
  431. #endif
  432.  
  433.  
  434. //
  435. // CollideWithBall fⁿhrt die Collision aus, die gerade zwischen dem Ball-Objekt
  436. // und dem ⁿbergebenen Ball stattfindet. Als Sonderbehandlung wird der SlowStep
  437. // eingeleitet, bzw. der Ball wird gestoppt, falls das Objekt selbst ⁿbergeben
  438. // wird.
  439. //
  440. void Ball::CollideWithBall( Ball *b ) {
  441. Vec2    x1,y1,x2,y2;
  442.  
  443.     if (this==b) {
  444. #if (SIM_SLOW)
  445.         SetV(v);                    // ruft indirekt den SlowStep auf
  446. #else
  447.         SetV(Vec2Zero);    // Stillstand erreicht
  448. #endif
  449.         return;
  450.     }
  451.  
  452. // Aufsplittung der Vektoren in Anteile parallel und orthogonal zur
  453. // Verbindungslinie der beiden Kugeln.
  454.  
  455.     V().Split( P()-b->P(), &x1, &y1 );
  456.     b->V().Split( P()-b->P(), &x2, &y2 );
  457.  
  458.     Vec2    help = M()*x1;
  459.                 help+= b->M()*x2;
  460.     Vec2    diff= x1-x2;
  461.     Vec2    x1n = (help - b->M()*diff ) / (M()+b->M());
  462.                 x1n += y1;
  463.     Vec2    x2n = (help +    M()*diff ) / (M()+b->M());
  464.                 x2n += y2;
  465.  
  466. //
  467. // wird bei stehenden Kugeln die Haftreibung ueberwunden ?
  468. //
  469.     if (!IsRunning()) {
  470. #ifdef DEBUG
  471.     if (debug&StickLevel) {
  472.             printf( "%g < %g ?\n", (double)x1n.SqrNorm(),
  473.                                     (double)(g->GetPresetHaft()*g->GetPresetHaft()) );
  474.     }
  475. #endif
  476.         if (x1n.SqrNorm()<g->GetPresetHaft()*g->GetPresetHaft()) {
  477.             x1n = Vec2Zero;
  478.             g->TouchedBall(this);
  479.         }
  480.     }
  481.     if (!b->IsRunning()) {
  482. #ifdef DEBUG
  483.     if (debug&StickLevel) {
  484.             printf( "%g < %g ?\n", (double)x2n.SqrNorm(),
  485.                                     (double)(g->GetPresetHaft()*g->GetPresetHaft()) );
  486.     }
  487. #endif
  488.         if (x2n.SqrNorm()<g->GetPresetHaft()*g->GetPresetHaft()) {
  489.             x2n = Vec2Zero;
  490.             g->TouchedBall(b);
  491.         }
  492.     }
  493. //
  494. // demnaechst fliegende Kugel zuerst bearbeiten
  495. //
  496.     if (!x1n.IsZero()) {
  497.         SetV(x1n);
  498.         b->SetV(x2n);
  499.     }
  500.     else {
  501.         b->SetV(x2n);
  502.         SetV(x1n);
  503.     }
  504.  
  505.     ClickBall();
  506.     WasHit(b);
  507. }
  508.  
  509. //
  510. // WasHit ist eine ⁿberlagerbare Funktion, die die Berechnung des Caches
  511. // anst÷▀t, wenn der Ball durch ein fremdes Objekt (Wand oder anderer Ball)
  512. // getroffen wurde.
  513. //
  514. void Ball::WasHit(Object *) {
  515. #if (TIME_CACHE)
  516.     CollisionCalc();
  517. #endif
  518.     if (pball)        pball->CueWasHit();
  519. }
  520.  
  521.  
  522. int Ball::Lock( PBallTop *p ) {
  523.     if (!pball) {
  524.         pball = p;
  525.         return 0;
  526.     }
  527.     else {
  528.         return 1;
  529.     }
  530. }
  531.  
  532. Real Ball::FindClosest( const Ball *myself, const Vec2 &pos, Ball **best ) {
  533. Ball    *ball;
  534. Real    dist;
  535. Real    min_dist=RealZero;
  536.  
  537.     *best=0l;
  538.     for (ball=(Ball*)DynObj::dyn_queue; ball; ball=(Ball*)ball->DynObj::next ) {
  539.         dist = (ball->P()-pos).SqrNorm();        // absoluter Wert egal
  540.         if ((ball!=myself)&&(!*best||dist<min_dist)) {
  541.             *best        = ball;                                // nΣchste Kugel
  542.             min_dist = dist;                                // bisheriger Minimalabstand
  543.         }
  544.     }
  545.  
  546.     return sqrt(min_dist);
  547. }
  548.  
  549. int Ball::FitsAt(const Vec2 &pos) {
  550. Ball *best;
  551. Real dist;
  552.  
  553.     dist = FindClosest(this,pos,&best);
  554.     return (dist<(2.0*R()+EPS))?0:1;
  555. }
  556.  
  557. int Ball::FitsNextTo(const Vec2 &pos, const Vec2 &delta, Vec2 *newpos ) {
  558.  
  559.     *newpos = pos;
  560.     do {
  561.         if (FitsAt(*newpos))        return 1;
  562.         *newpos += delta;
  563.     }
  564.     while(1);        // the check against the table boundary is missing
  565. }
  566.  
  567.  
  568. int Ball::Unlock( PBallTop *q ) {
  569.     if (pball==q) {
  570.         pball = 0;
  571.         return 0;
  572.     }
  573.     else {
  574.         return 1;
  575.     }
  576. }
  577.  
  578. void Ball::GoUnlocked() {
  579.     if (pball)    pball->StopMoving();
  580. }
  581.  
  582. void Ball::Draw() {
  583.     state->Show();
  584. }
  585.